package com.atguigu.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.respority.HospitalRepository;
import com.atguigu.respority.ScheduleRepository;
import com.atguigu.service.DepartmentService;
import com.atguigu.service.HospitalService;
import com.atguigu.service.ScheduleService;
import com.atguigu.yygh.model.hosp.BookingRule;
import com.atguigu.yygh.model.hosp.Department;
import com.atguigu.yygh.model.hosp.Hospital;
import com.atguigu.yygh.model.hosp.Schedule;
import com.atguigu.yygh.vo.hosp.BookingScheduleRuleVo;
import com.atguigu.yygh.vo.hosp.ScheduleOrderVo;
import com.atguigu.yygh.vo.hosp.ScheduleQueryVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;

import org.springframework.stereotype.Service;
import org.joda.time.DateTime;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ScheduleServiceImpl implements ScheduleService {
    @Autowired
    private ScheduleRepository scheduleRepository;

    @Override
    public void save(Map<String, Object> paramMap) {
        //进行数据转换
        String s = JSONObject.toJSONString(paramMap);
        Schedule schedule = JSONObject.parseObject(s, Schedule.class);
        //查看排班是否存在  如果存在就跟新  如果不存在就添加
        Schedule scheduleId = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(schedule.getHoscode(), schedule.getHosScheduleId());
        if (scheduleId == null) {
            schedule.setCreateTime(new Date());
            schedule.setUpdateTime(new Date());
            schedule.setStatus(1);
        } else {
            schedule.setId(scheduleId.getId());
            schedule.setUpdateTime(scheduleId.getUpdateTime());
            schedule.setStatus(scheduleId.getStatus());
            schedule.setCreateTime(scheduleId.getCreateTime());
        }
        scheduleRepository.save(schedule);

    }

    @Override
    public Page<Schedule> selectPage(Integer pageNum, Integer limit, ScheduleQueryVo scheduleQueryVo) {
        //排序加分页
        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        PageRequest pageRequest = PageRequest.of(pageNum - 1, limit, sort);
        //查询条件
        Schedule schedule = new Schedule();
        BeanUtils.copyProperties(scheduleQueryVo, schedule);
        //模糊匹配查询
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING).withIgnoreCase(true);
        //创建 Example对象
        Example<Schedule> example = Example.of(schedule, exampleMatcher);
        //调用 findAll方法
        Page<Schedule> pages = scheduleRepository.findAll(example, pageRequest);
        return pages;
    }

    @Override
    public void remove(String hoscode, String hosScheduleId) {
        Schedule schedule = scheduleRepository.getScheduleByHoscodeAndHosScheduleId(hoscode, hosScheduleId);
        if (schedule != null) {
            scheduleRepository.deleteById(schedule.getId());
        }
    }


    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private HospitalRepository hospitalRepository;
    @Autowired
    private HospitalService hospitalService;
    @Autowired
    private DepartmentService departmentService;

    @Override
    public Map<String, Object> getScheduleRule(long page, long limit, String hoscode, String depcode) {
        //根据 hoscode 和 depcode来查询
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);
        //根据工作日期来排班
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")//按照那个属性进行分组
                        .first("workDate").as("workDate")//把第一个workDate的属性取出来，赋值给BookingScheduleRuleVo中的workDate属性
                        .count().as("docCount")//每一组的排班数量
                        .sum("reservedNumber").as("reservedNumber")  //对 剩余的预约号求和
                        .sum("availableNumber").as("availableNumber"), //对预约人数求和
                Aggregation.sort(Sort.Direction.ASC, "workDate"),  //排序
                Aggregation.skip((page - 1) * limit),   //分页
                Aggregation.limit(limit));
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggregate.getMappedResults();
        bookingScheduleRuleVoList.forEach(bookingScheduleRuleVo -> {
            bookingScheduleRuleVo.setWorkDateMd(bookingScheduleRuleVo.getWorkDate());
            bookingScheduleRuleVo.setDayOfWeek(this.getDayOfWeek(new DateTime(bookingScheduleRuleVo.getWorkDate())));
        });
        //总日期个数
        Integer total = this.getTotal(hoscode, depcode);
        //前端还需要一个医院名称
        String hosname = hospitalRepository.findByHoscode(hoscode).getHosname();
        Map<String, Object> result = new HashMap<>();
        result.put("total", total);
        result.put("bookingScheduleRuleList", bookingScheduleRuleVoList);

        Map<String, String> baseMap = new HashMap<>();
        baseMap.put("hosname", hosname);
        result.put("baseMap", baseMap);
        return result;

    }

    @Override
    public List<Schedule> getDetailSchedule(String hoscode, String depcode, String workDate) {
        //1、hoscode+depcode+workDate 查询排班列表
        // toDate()方法是将 传过来的 String类型的workDate转换为 Date类型
        List<Schedule> scheduleList = scheduleRepository.findScheduleByHoscodeAndDepcodeAndWorkDate(
                hoscode, depcode, new DateTime(workDate).toDate());
        //2、schedule.param （医院名称、科室名称、日期对应星期）
        scheduleList.forEach(item -> {
            this.packSchedule(item);
        });
        return scheduleList;

    }

    //日期查询
    @Override
    public Map<String, Object> getBookingScheduleRule(Integer page, Integer limit, String hoscode, String depcode) {
        //1、根据医院编号查询医院对象，预约规则
        Hospital hospital = hospitalRepository.findByHoscode(hoscode);
        BookingRule bookingRule = hospital.getBookingRule();

        //2、私有方法  IPage（mp）， 查询当前页的日期对象
        IPage iPage = this.getListDate(page,limit,bookingRule);

        List<Date> pageDateList = iPage.getRecords();//当前页日期对象
        long pages = iPage.getPages();//总页数
        long total = iPage.getTotal();


        //每一个日期，对应一个BookingScheduleRuleVo-->每个属性要有值
        //workDate相同的一组排班，统计一些数据，封装到BookingScheduleRuleVo对象中

        //针对指定医院指定科室下的排班按照workDate分组

        //写in查询  ，不要写成is
        //自己做点测试数据
        //3、查询当前页日期集合对应的排班集合，进行分组聚合
        Criteria criteria = Criteria.where("hoscode").is(hoscode)
                .and("depcode").is(depcode).and("workDate").in(pageDateList);

        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),//针对指定的排班进行分组聚合统计
                Aggregation.group("workDate")//
                        .count().as("docCount")//每一组的排班数量，赋值给BookingScheduleRuleVo中的docCount属性
                        .first("workDate").as("workDate")//这一组排班中的第一个排班的workDate取出，赋值给BookingScheduleRuleVo中的workDate属性
                        .sum("reservedNumber").as("reservedNumber")//这一组排班reservedNumber属性值的总和赋值给BookingScheduleRuleVo中reservedNumber属性
                        .sum("availableNumber").as("availableNumber")
        );

        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);

        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggregate.getMappedResults();

//        for (BookingScheduleRuleVo bookingScheduleRuleVo : bookingScheduleRuleVoList) {
//            Date key = bookingScheduleRuleVo.getWorkDate();
//            map.put(key,bookingScheduleRuleVo);
//        }


        //16---22号，7个日期，每个日期对应的BookingScheduleRuleVo对象
        //但是目前只有18和20号有对应的vo对象，创建默认的rulevo对象

        //4、bookingScheduleRuleVoList转成map ， key：workDate ， value：ruleVo对象本身
        //  16-22 ， 根据每一个日期从list集合中查询对应的ruleVo对象
        Map<Date, BookingScheduleRuleVo> map = bookingScheduleRuleVoList.stream().collect(Collectors.toMap(
                BookingScheduleRuleVo::getWorkDate, bookingScheduleRuleVo -> bookingScheduleRuleVo
        ));

//        int n = 0;
//        for (Date date : pageDateList) {


        List<BookingScheduleRuleVo> bookingScheduleRuleList = new ArrayList<>();
        for (int i = 0 ; i < pageDateList.size() ; i++) {

            Date date = pageDateList.get(i);

//            BookingScheduleRuleVo ruleVo = this.getRuleVoByDate(bookingScheduleRuleVoList,date);
            BookingScheduleRuleVo ruleVo = map.get(date);
            if( ruleVo == null ){
                //创建一个默认的ruleVo对象
                ruleVo = new BookingScheduleRuleVo();
                ruleVo.setDocCount(0);//排班的数量
                ruleVo.setWorkDate(date);
                ruleVo.setReservedNumber(-1);
                ruleVo.setAvailableNumber(-1);//没有排班
            }

            //额外的字段需要赋值
            ruleVo.setWorkDateMd(date);//另一种日期格式
            ruleVo.setDayOfWeek(this.getDayOfWeek(new DateTime(date)));//星期

//            状态
//            0：正常
//            1：即将放号
//            -1：当天已停止挂号

            //最后一页的最后一条，显示即将放号   0  6
//            if(page==pages && n == pageDateList.size()-1){
//
//            }
//            n++;

            if(page==pages && i==pageDateList.size()-1){
                ruleVo.setStatus(1);
            }else{
                ruleVo.setStatus(0);
            }

            //当天已经停止挂号 status=-1
            //第一页的第一条
            if( page==1 && i==0 ){
                String stopTime = bookingRule.getStopTime();
                DateTime dateTime = this.getDateTime(new Date(), stopTime);//11:30

                if(dateTime.isBeforeNow()){
                    ruleVo.setStatus(-1);
                }
            }

            //
            bookingScheduleRuleList.add(ruleVo);

        }


        Map<String,Object> result = new HashMap<>();
        result.put("bookingScheduleList",bookingScheduleRuleList);
        result.put("total",total);//总日期个数


        Map<String,Object> baseMap = new HashMap<>();

        baseMap.put("hosname",hospital.getHosname());

        //depcode
        Department department = departmentService.getDepartment(hoscode,depcode);
        baseMap.put("bigname",department.getBigname());//大科室名称
        baseMap.put("depname",department.getDepname());//小科室名称
        baseMap.put("workDateString",new DateTime().toString("yyyy年MM月"));//2022年11月
        baseMap.put("releaseTime",bookingRule.getReleaseTime());//放号时间
        baseMap.put("stopTime",bookingRule.getStopTime());

        result.put("baseMap",baseMap);

        return result;
        }

    /**
     * 排班信息
     * @param id
     * @return
     */
    @Override
    public Schedule getById(String id) {
        Schedule schedule = scheduleRepository.findById(id).get();
        this.packSchedule(schedule);
        return schedule;
    }

    @Override
    public ScheduleOrderVo getScheduleOrderVo(String scheduleId) {
        ScheduleOrderVo  scheduleOrderVo =new ScheduleOrderVo();
        //查询排班
        Schedule schedule = scheduleRepository.findById(scheduleId).get();
        //查看预约法则
        Hospital hospital = hospitalService.getHoscode(schedule.getHoscode());
        BookingRule bookingRule = hospital.getBookingRule();
        //赋值
        scheduleOrderVo.setHoscode(hospital.getHoscode());
        scheduleOrderVo.setHosname(hospitalService.getHospName(schedule.getHoscode()));        scheduleOrderVo.setDepcode(schedule.getDepcode());
        scheduleOrderVo.setDepname(departmentService.getDepName(schedule.getHoscode(), schedule.getDepcode()));
        scheduleOrderVo.setHosScheduleId(schedule.getHosScheduleId());
        scheduleOrderVo.setAvailableNumber(schedule.getAvailableNumber());
        scheduleOrderVo.setTitle(schedule.getTitle());
        scheduleOrderVo.setReserveDate(schedule.getWorkDate());
        scheduleOrderVo.setReserveTime(schedule.getWorkTime());
        scheduleOrderVo.setAmount(schedule.getAmount());
        //退号时间截至天数
        Integer quitDay = bookingRule.getQuitDay();
        DateTime dateTime = this.getDateTime(new DateTime(schedule.getWorkDate()).plusDays(quitDay).toDate(), bookingRule.getQuitTime());

        scheduleOrderVo.setQuitTime(dateTime.toDate());
        //预约开始时间
        DateTime startTime = this.getDateTime(new Date(), bookingRule.getReleaseTime());
        scheduleOrderVo.setStartTime(startTime.toDate());
        //预约截止时间
        DateTime endTime = this.getDateTime(new DateTime().plusDays(bookingRule.getCycle()).toDate(), bookingRule.getStopTime());
        scheduleOrderVo.setStopTime(endTime.toDate());
        //当天停止挂号时间
        DateTime stopTime = this.getDateTime(new Date(), bookingRule.getStopTime());
        scheduleOrderVo.setStopTime(stopTime.toDate());
        return   scheduleOrderVo;
    }

    /**
     * 更新排班
     * @param schedule
     */
    @Override
    public void update(Schedule schedule) {
        //目的更新 mgdb数据库里面的排班数据
          schedule.setUpdateTime(new Date());
          scheduleRepository.save(schedule);
    }

    /**
     * 将Date日期（yyyy-MM-dd HH:mm）转换为DateTime
     */
    private DateTime getDateTime(Date date, String timeString) {
        String  dateTimeString = new DateTime(date).toString("yyyy-MM-dd")+" "+timeString;
        DateTime dateTime = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(dateTimeString);
        return dateTime;
    }

    // 当天（第一页第一条） 如果过了停挂时间 status=-1 ，当天已停止挂号

    /**
     * 获取可预约周期的日期
     * @param page
     * @param limit
     * @param bookingRule
     * @return
     */
    private IPage getListDate(Integer page, Integer limit, BookingRule bookingRule) {
        //1 预约周期
        Integer cycle = bookingRule.getCycle();
        //当天的放号时间
        DateTime dateTime = this.getDateTime(new Date(), bookingRule.getReleaseTime());
         //已经开始放号
        if(dateTime.isBeforeNow()){
            cycle+=1;
        }
        //按照预约周期创建cycle个日期对象
        List<Date> dateList=new ArrayList<>();
        //根据 cycle创建周期
        for (Integer i = 0; i <cycle ; i++) {
            Date date =new DateTime().plusDays(i).toDate(); //带有时分秒
            String yyyyMMdd = new DateTime(date).toString("yyyy-MM-dd");
            Date workDate = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime(yyyyMMdd).toDate();
            dateList.add(workDate);
        }
        //分页 page=1,limit=7
        int begin=(page-1)*limit; //0
        int end=(page-1)*limit+limit; //7
         //最后一条·不够七条
        if(end>dateList.size()){
            end =dateList.size();
        }
        List<Date> pageDateList  =new ArrayList<>();
        for (int i = begin; i <end ; i++) {
            Date date = dateList.get(i);
            pageDateList.add(date);
        }
        com.baomidou.mybatisplus.extension.plugins.pagination.Page datePage =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page(page, limit, dateList.size());
        datePage.setRecords(pageDateList);//当前页数

        return  datePage;
    }

    private void packSchedule(Schedule schedule) {
        //医院名称
        schedule.getParam().put("hosname", hospitalService.getHospName(schedule.getHoscode()));
        //科室名称
        schedule.getParam().put("depname", departmentService.getDepName(schedule.getHoscode(), schedule.getDepcode()));
        //日期对应星期
        schedule.getParam().put("dayOfWeek", this.getDayOfWeek(new DateTime(schedule.getWorkDate())));
    }

    private Integer getTotal(String hoscode, String depcode) {
        Criteria criteria = Criteria.where("hoscode").is(hoscode).and("depcode").is(depcode);
        Aggregation aggregation = Aggregation.newAggregation(
                Aggregation.match(criteria),
                Aggregation.group("workDate")
        );
        AggregationResults<BookingScheduleRuleVo> aggregate = mongoTemplate.aggregate(aggregation, Schedule.class, BookingScheduleRuleVo.class);
        List<BookingScheduleRuleVo> bookingScheduleRuleVoList = aggregate.getMappedResults();
        int total = bookingScheduleRuleVoList.size();
        return total;

    }

    private String getDayOfWeek(DateTime dateTime) {
        List<String> strings = Arrays.asList("周一", "周二", "周三", "周四", "周五", "周六", "周日");
        int dayOfWeek = dateTime.getDayOfWeek();
        return strings.get(dayOfWeek - 1);
    }
}
